package com.example;

import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author twj
 * @description stream流案例
 * @time 2023/9/2^16:46:25
 */
public class StreamDemo {
    public static void main(String[] args) {
        List<Author> authors = getAuthors();
        //基本操作
        test01(authors);

        //1 - 单列集合创建流
        Stream<Author> stream = authors.stream();
        //2 - 数组创建流
        test02();
        //3 - 双列创建流
        test03();

        //中间操作！！！！！！！！！！
        //1 - filter: 可以对流中的元素进行条件过滤，符合过滤条件的才能继续留在流中
        test04(authors);
        //2 - map: 可以对流中的元素进行计算或转换 （只能由一个对象转换为另一个对象）
        test05(authors);
        //3 - distinct: 可以去除流中的重复元素
        //注意：由于distinct方法是依赖Object的equals方法来判断是否是相同对象的，所以需要注意重写equals方法
        test06(authors);
        //4 - sorted: 可以对流中的元素进行排序
        test07(authors);
        //5 - limit: 可以设置流的最大长度，超出部分将被抛弃
        test08(authors);
        //6 - skip: 跳过流中的前n个元素，返回剩下的元素
        test09(authors);
        //7 - flatMap: 可以把一个对象转换成多个对象作为流中的元素 （本质上就是展开数据！可以将一个对象转换成多个对象）
        test10(authors);

        //终结操作！！！！！！！！！！
        //1 - forEach: 对流中元素进行遍历操作，通过传参去指定对遍历的元素进行什么样的操作
        test11(authors);
        //2 - count: 可以用来获取当前流中元素的个数
        test12(authors);
        //3 - max&min: 可以用来获取流中的最值
        test13(authors);
        //4 - collect: 把当前流转换成一个集合
        test14(authors);
        //终结操作->查找与匹配！！！！
        //5 - anyMatch: 可以用来判断是否有任意符合匹配条件的元素，结果为boolean类型
        test15(authors);
        //6 - allMatch: 可以用来判断是否都符合匹配条件（都符合结果：true，否则：false）
        test16(authors);
        //7 - noneMatch: 可以判断流中的元素是否都不符合匹配条件（都不符合结果：true，否则：false）
        test17(authors);
        //8 - findAny: 获取流中的任意一个元素，该方法没有办法保证获取的一定是流中的第一个元素
        test18(authors);
        //9 - findFirst: 获取流中的第一个元素
        test19(authors);

        //10 - reduce: 归并，对流中的数据按照我们制定的计算方式计算出一个结果
        //reduce的作用是把stream中的元素组合起来，我们可以传入一个初始值，它会按照我们的计算方式
        //依次拿流中的元素在初始化值的基础上进行计算，计算结果再和后面的元素计算。
        /* 它内部的计算方式如下：
         * T result = identity;
         * for (T element : this.stream)
         *      result = accumulator.apply(result,element)
         * return result;
         * 其中identity就是我们可以通过方法参数传入的初始值，accumulator的apply具体进行什么计算也是我们通过方法参数来确定的
         */
        test20(authors);

        /*
         * stream总结：
         *   - 惰性求值（如果没有终结操作，没有中间操作是不会得到执行的)
         *   - 流是一次性的（一旦一个流对象经过一个终结操作后，这个流就不能再被使用)
         *   - 不会影响原数据（我们在流中可以对数据做很多处理，但是正常情况下是不会影响原来集合中的元素的，这往往也是我们期望的)
         *
         */

    }

    private static void test20(List<Author> authors) {
        //使用reduce求所有作者年龄的和
        Integer sum = authors.stream()
                .map(author -> author.getAge())
                .reduce(0, (integer, integer2) -> integer + integer2);
        System.out.println("年龄的和：" + sum);
        //使用reduce求所有作者中年龄的最大值
        Integer max = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MIN_VALUE, (result, element) -> result < element ? element : result);
        System.out.println("最大值：" + max);
        //使用reduce求所有作者中年龄的最小值
        Integer min = authors.stream()
                .map(author -> author.getAge())
                .reduce(Integer.MAX_VALUE, (result, element) -> result > element ? element : result);
        System.out.println("最小值：" + min);
        //第二种方式求最小值
        Optional<Integer> minOptional = authors.stream()
                .map(author -> author.getAge())
                .reduce((result, element) -> result > element ? element : result);
        minOptional.ifPresent(age -> System.out.println("一个参数的最小值：" + age));

    }

    private static void test19(List<Author> authors) {
        //获取一个年龄最小的作家，并输出他的姓名
        Optional<Author> first = authors.stream()
                .sorted((o1, o2) -> o1.getAge() - o2.getAge())
                .findFirst();
        first.ifPresent(author -> System.out.println(author.getName()));
    }

    private static void test18(List<Author> authors) {
        //获取任意一个年龄大于18的作家，如果存在就输出他的名字
        Optional<Author> any = authors.stream()
                .filter(author -> author.getAge() > 18)
                .findAny();
        //如果存在就做某些处理
        any.ifPresent(author -> System.out.println(author.getName()));
    }

    private static void test17(List<Author> authors) {
        //判断作家是否都没有超过100岁的
        boolean b = authors.stream()
                .noneMatch(author -> author.getAge() >= 100);
        System.out.println(b); //true
    }

    private static void test16(List<Author> authors) {
        //判断是否所有的作家都是成年人
        boolean b = authors.stream()
                .allMatch(author -> author.getAge() >= 18);
        System.out.println(b); //false
    }

    private static void test15(List<Author> authors) {
        //判断是否有年龄在29以上的作家
        boolean b = authors.stream()
                .anyMatch(author -> author.getAge() > 29);
        System.out.println(b); //true
    }

    private static void test14(List<Author> authors) {
        //获取一个存放所有作者名字的List集合
        List<String> nameList = authors.stream()
                .map(author -> author.getName())
                .distinct()
                .collect(Collectors.toList());
        System.out.println(nameList);
        //获取一个所有书名的Set集合
        Set<Book> nameSet = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .collect(Collectors.toSet());
        nameSet.forEach(book -> System.out.println(book.getName()));
        //获取一个Map集合，Map的key为作者名，value为List<Book>
        Map<String, List<Book>> map = authors.stream()
                .distinct()
                .collect(Collectors.toMap(author -> author.getName(), author -> author.getBooks()));
        System.out.println(map);
    }

    private static void test13(List<Author> authors) {
        //分别获取这些作家的所出书籍的最高分和最低分并打印
        Optional<Integer> max = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((score1, score2) -> score1 - score2);
        Optional<Integer> min = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .map(book -> book.getScore())
                .max((score1, score2) -> score2 - score1);
        System.out.println("最大值：" + max.get() + " 最小值：" + min.get());

    }

    private static void test12(List<Author> authors) {
        //打印这些作家的所出书籍的数目，需要删除重复元素
        long count = authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .count();
        System.out.println(count);
    }

    private static void test11(List<Author> authors) {
        //输出所有作家的名字
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));
    }

    private static void test10(List<Author> authors) {
        //打印所有书籍的名字，要求对重复的元素进行去重
        authors.stream()
                .flatMap((Function<Author, Stream<Book>>) author -> {
                    return author.getBooks().stream(); //转换成流，流中每个元素就是一个book对象
                })
                .distinct() //去重
                .forEach(book -> System.out.println(book.getName()));
        //打印现有书籍的所有分类，要求对分类进行去重，不能出现这种格式：哲学，爱情
        authors.stream()
                .flatMap(author -> author.getBooks().stream())
                .distinct()
                .flatMap(book -> Stream.of(book.getCategory().split(",")))
                .distinct()
                .forEach(category -> System.out.println(category));

    }

    private static void test09(List<Author> authors) {
        //打印除了年龄最大的作家外的其他作家，要求不能有重复元素，并且按年龄降序排序
        authors.stream()
                .distinct()
                .sorted()
                .skip(2)
                .forEach(author -> System.out.println(author.getName()));
    }

    private static void test08(List<Author> authors) {
        //对流中的元素按年龄进行降序排序，并要求不能有重复的元素，然后打印其中年龄最大的两个作家的姓名
        authors.stream()
                .distinct()
                .sorted()
                .limit(2)
                .forEach(author -> System.out.println(author.getName()));
    }

    private static void test07(List<Author> authors) {
        //对流中的元素按年龄进行降序排序，并且要求不能有重复的元素
        authors.stream()
                .distinct()
                .sorted((o1, o2) -> o2.getAge() - o1.getAge()) //不传参就需要流中对象实现comparable接口，并重写compareTo方法
                .forEach(author -> System.out.println(author.getAge()));
    }

    private static void test06(List<Author> authors) {
        //打印所有作家的姓名，并要求其中不能有重复元素
        authors.stream()
                .distinct()
                .forEach(author -> System.out.println(author.getName()));
    }

    private static void test05(List<Author> authors) {
        //打印所有作家的姓名
        authors.stream()
                .map(author -> author.getName())
                .forEach(name -> System.out.println(name));
        //将所有作家年龄+10然后打印出来
        authors.stream()
                .map(author -> author.getAge())
                .map(age -> age + 10)
                .forEach(age -> System.out.println(age));
    }

    private static void test04(List<Author> authors) {
        //打印所有姓名长度大于1的作家的姓名
        authors.stream()
                .filter(author -> author.getName().length() > 1)
                .forEach(author -> System.out.println(author.getName()));
    }

    private static void test03() {
        Map<String, Integer> map = new HashMap<>();
        map.put("蜡笔小新", 15);
        map.put("黑子", 17);
        map.put("日向阳", 16);

        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        Stream<Map.Entry<String, Integer>> stream = entrySet.stream();

        stream.filter(new Predicate<Map.Entry<String, Integer>>() {
                    @Override
                    public boolean test(Map.Entry<String, Integer> entry) {
                        return entry.getValue() > 16;
                    }
                })
                .forEach(new Consumer<Map.Entry<String, Integer>>() {
                    @Override
                    public void accept(Map.Entry<String, Integer> entry) {
                        System.out.println(entry.getKey() + "===" + entry.getValue());
                    }
                });
    }

    private static void test02() {
        Integer[] arr = {1, 2, 3, 4, 5};
        //Stream<Integer> stream = Arrays.stream(arr);
        Stream<Integer> stream = Stream.of(arr);
        stream.distinct()
                .filter(integer -> integer > 2)
                .forEach(integer -> System.out.println(integer));
    }

    private static void test01(List<Author> authors) {
        authors.stream() //把集合转换成流
                .distinct() //去重
                .filter(author -> author.getAge() < 18) //筛选
                .forEach(author -> System.out.println(author.getName())); //遍历输出
    }

    private static List<Author> getAuthors() {
        //数据初始化
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        Author author2 = new Author(2L, "亚拉索", 15, "狂风也追逐不上他的思考速度", null);
        Author author3 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
        Author author4 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);

        //书籍列表
        List<Book> books1 = new ArrayList();
        List<Book> books2 = new ArrayList();
        List<Book> books3 = new ArrayList();

        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));

        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(4L, "吹或不吹", "爱情,个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new Book(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList(Arrays.asList(author, author2, author3, author4));
        return authorList;
    }
}
